package com.study.spring.proxy.core;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.study.spring.proxy.util.EmptyUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.*;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;

import java.io.IOException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

@Slf4j
public class SaosHttpClientInvocationHandler implements InvocationHandler {


    private ObjectMapper objectMapper = new ObjectMapper();

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        /**
         * 接口中方法的返回值类型,用户反序列化成java对象
         */
        Class<?> returnType = method.getReturnType();

        /**
         * 使用springmvc的注解，减少学习成本
         */
        RequestMapping requestMapping = method.getAnnotation(RequestMapping.class);

        /**
         * 取出请求的url
         */
        String httpUrl = getHttpUrl(requestMapping);

        /**
         * 取出请求类型
         */
        RequestMethod requestMethod = getRequestMethod(requestMapping);

        /**
         * 处理请求的参数
         */
        Parameter[] parameters = method.getParameters();

        /**
         * 处理路径参数
         */
        Map<String, Object> pathVariableMap = doPathVariable(args, parameters);

        /**
         * 请求头信息
         */
        HttpHeaders httpHeads = doHttpHeads(args, parameters);


        /**
         * 使用RestTemplate请求远程的接口
         * (也可以换成OKhttp或者apache的httpclient都可以)
         */
        RestTemplate restTemplate = new RestTemplate();

        /**
         * 判断是GET还是POST
         * 是GET就用GET的方式请求
         * 是POST就用POST的方式请求
         */
        if(RequestMethod.GET.equals(requestMethod)){
            /**
             * 处理一下get请求的参数,把参数拼接url后面
             */
            httpUrl = doGetParameter(args, httpUrl, parameters);

            /**
             * get请求,body为空
             */
            HttpEntity<String> requestEntity = new HttpEntity<>(null, httpHeads);

            /**
             * 执行请求
             */
            ResponseEntity<?> responseEntity = restTemplate.exchange(httpUrl, HttpMethod.GET, requestEntity, returnType,pathVariableMap);

            /**
             * 如果http协议返回200表示请求成功
             */
            if(responseEntity.getStatusCode().equals(HttpStatus.OK)){
                return responseEntity.getBody();
            }else{
                /**
                 * 如果http协议返回非200,表示请求失败了
                 * 经常见到返回404或者返回500,都会进入这里
                 * 我们使用的Feign也是同样的逻辑,返回非200就会抛出异常
                 */
                log.error("请求:{},响应非200,返回结果={}",httpUrl,responseEntity.toString());
                throw new RuntimeException(httpUrl+"response fail");
            }

        }
        if(RequestMethod.POST.equals(requestMethod)){
            HttpEntity requestEntity = doPostParameter(args, parameters, httpHeads);
            ResponseEntity<?> responseEntity = restTemplate.postForEntity(httpUrl, requestEntity, returnType, pathVariableMap);
            if(responseEntity.getStatusCode().equals(HttpStatus.OK)){
                return responseEntity.getBody();
            }else{
                /**
                 * 如果http协议返回非200,表示请求失败了
                 * 经常见到返回404或者返回500,都会进入这里
                 * 我们使用的Feign也是同样的逻辑,返回非200就会抛出异常
                 */
                log.error("请求:{},响应非200,返回结果={}",httpUrl,responseEntity.toString());
                throw new RuntimeException(httpUrl+"response fail");
            }
        }
        log.error("只支持GET或者POST请求,其他请求方式不支持");
        throw new RuntimeException("不支持的请求方式");
    }

    /**
     * 处理路径参数
     * @param args
     * @param parameters
     * @param
     */
    private Map<String, Object> doPathVariable(Object[] args, Parameter[] parameters){
        Map<String, Object> pathVariableMap = new HashMap<String, Object>();
        if(EmptyUtil.isEmpty(parameters)){
            return pathVariableMap;
        }
        for(int i=0;i<parameters.length;i++){
            Parameter parameter = parameters[i];
            // 处理PathVariable路径参数
            boolean hasPathVariable = parameter.isAnnotationPresent(PathVariable.class);
            if(hasPathVariable){
                PathVariable pathVariable = parameter.getAnnotation(PathVariable.class);
                String pathVariableKey = pathVariable.value();
                pathVariableMap.put(pathVariableKey,args[i]);
            }
        }
        return pathVariableMap;
    }

    /**
     * 处理请求头信息
     * @param args
     * @param parameters
     * @return
     */
    private HttpHeaders doHttpHeads(Object[] args, Parameter[] parameters){
        HttpHeaders httpHeaders = new HttpHeaders();
        if(EmptyUtil.isNotEmpty(parameters)){
            for(int i=0;i<parameters.length;i++){
                Parameter parameter = parameters[i];
                boolean hasHeaders = parameter.isAnnotationPresent(SaosHeaders.class);
                if(hasHeaders && EmptyUtil.isNotEmpty(args[i])){
                    try {
                        String jsonString = objectMapper.writeValueAsString(args[i]);
                        Map<String,String> headMap = objectMapper.readValue(jsonString, Map.class);
                        Set<String> keySet = headMap.keySet();
                        for (String key : keySet) {
                            httpHeaders.set(key,headMap.get(key));
                        }
                    } catch (IOException e) {
                        log.error("反序列化对象异常,异常原因",e);
                        throw new RuntimeException(e.toString());
                    }
                }
            }
        }
        return httpHeaders;
    }

    private HttpEntity doPostParameter(Object[] args, Parameter[] parameters, HttpHeaders httpHeaders) {
        if(EmptyUtil.isNotEmpty(parameters)){
            for(int i=0;i<parameters.length;i++){
                Parameter parameter = parameters[i];
                boolean hasRequestBody = parameter.isAnnotationPresent(RequestBody.class);
                if(hasRequestBody){
                    /**
                     * 说明请求方式是post/json提交,设置一下contentType
                     */
                    httpHeaders.setContentType(MediaType.APPLICATION_JSON);
                    Object postValue = args[i];
                    try {
                        String jsonString = objectMapper.writeValueAsString(postValue);
                        HttpEntity requestEntity = new HttpEntity(jsonString, httpHeaders);
                        return requestEntity;
                    } catch (JsonProcessingException e) {
                        log.error("反序列化对象异常,异常原因",e);
                        throw new RuntimeException(e.toString());
                    }
                }
                boolean hasRequestParam = parameter.isAnnotationPresent(RequestParam.class);
                if(hasRequestParam){
                    /**
                     * 说明请求是post/表单提交，设置contentType
                     */
                    httpHeaders.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
                    MultiValueMap<String, String> params= new LinkedMultiValueMap<String, String>();
                    Object requestParsmMap = args[i];
                    try {
                        String jsonString = objectMapper.writeValueAsString(requestParsmMap);
                        Map<String,String> requestParam = objectMapper.readValue(jsonString, Map.class);
                        Set<String> keys = requestParam.keySet();
                        for (String key : keys) {
                            params.add(key,requestParam.get(key));
                        }
                        HttpEntity<MultiValueMap<String, String>> requestEntity = new HttpEntity<MultiValueMap<String, String>>(params, httpHeaders);
                        return requestEntity;
                    } catch (Exception e) {
                        log.error("反序列化对象异常,异常原因",e);
                        throw new RuntimeException(e.toString());
                    }
                }
            }
        }
        log.info("post无参数提交");
        HttpEntity requestEntity = new HttpEntity(null,httpHeaders);
        return requestEntity;
    }

    private String doGetParameter(Object[] args, String httpUrl, Parameter[] parameters) {
        if(EmptyUtil.isNotEmpty(parameters)){
            for(int i=0;i<parameters.length;i++){
                Parameter parameter = parameters[i];
                // 处理requestParam
                boolean hasRequestParam = parameter.isAnnotationPresent(RequestParam.class);
                if(hasRequestParam){
                    RequestParam requestParam = parameter.getAnnotation(RequestParam.class);
                    String key = requestParam.value();
                    Object value = args[i];

                    boolean contains = httpUrl.contains("?");
                    if(contains){
                        httpUrl = httpUrl + "&" + key+"="+value.toString();
                    }else{
                        httpUrl = httpUrl + "?" + key + "="+value.toString();
                    }
                }
            }
        }
        return httpUrl;
    }

    private RequestMethod getRequestMethod(RequestMapping requestMapping) {
        RequestMethod[] requestMethods = requestMapping.method();
        RequestMethod requestMethod = requestMethods[0];
        return requestMethod;
    }

    private String getHttpUrl(RequestMapping requestMapping) {
        String[] httpUrls = requestMapping.value();
        String httpUrl = httpUrls[0];
        return httpUrl;
    }
}
